home *** CD-ROM | disk | FTP | other *** search
/ Chip 2005 August (Alt) / CHIP 2005-08.1.iso / program / guvenlik / syslinux-3.07.exe / pxelinux.asm < prev    next >
Encoding:
Assembly Source File  |  2005-01-11  |  58.2 KB  |  2,607 lines

  1. ; -*- fundamental -*- (asm-mode sucks)
  2. ; $Id: pxelinux.asm,v 1.167 2005/01/12 00:34:54 hpa Exp $
  3. ; ****************************************************************************
  4. ;
  5. ;  pxelinux.asm
  6. ;
  7. ;  A program to boot Linux kernels off a TFTP server using the Intel PXE
  8. ;  network booting API.  It is based on the SYSLINUX boot loader for
  9. ;  MS-DOS floppies.
  10. ;
  11. ;   Copyright (C) 1994-2005  H. Peter Anvin
  12. ;
  13. ;  This program is free software; you can redistribute it and/or modify
  14. ;  it under the terms of the GNU General Public License as published by
  15. ;  the Free Software Foundation, Inc., 53 Temple Place Ste 330,
  16. ;  Boston MA 02111-1307, USA; either version 2 of the License, or
  17. ;  (at your option) any later version; incorporated herein by reference.
  18. ; ****************************************************************************
  19.  
  20. %define IS_PXELINUX 1
  21. %include "macros.inc"
  22. %include "config.inc"
  23. %include "kernel.inc"
  24. %include "bios.inc"
  25. %include "tracers.inc"
  26. %include "pxe.inc"
  27. %include "layout.inc"
  28.  
  29. ;
  30. ; Some semi-configurable constants... change on your own risk.
  31. ;
  32. my_id        equ pxelinux_id
  33. FILENAME_MAX_LG2 equ 7            ; log2(Max filename size Including final null)
  34. FILENAME_MAX    equ (1 << FILENAME_MAX_LG2)
  35. NULLFILE    equ 0            ; Zero byte == null file name
  36. NULLOFFSET    equ 4            ; Position in which to look
  37. REBOOT_TIME    equ 5*60        ; If failure, time until full reset
  38. %assign HIGHMEM_SLOP 128*1024        ; Avoid this much memory near the top
  39. MAX_OPEN_LG2    equ 5            ; log2(Max number of open sockets)
  40. MAX_OPEN    equ (1 << MAX_OPEN_LG2)
  41. PKTBUF_SIZE    equ (65536/MAX_OPEN)    ; Per-socket packet buffer size
  42. TFTP_PORT    equ htons(69)        ; Default TFTP port 
  43. PKT_RETRY    equ 6            ; Packet transmit retry count
  44. PKT_TIMEOUT    equ 12            ; Initial timeout, timer ticks @ 55 ms
  45. ; Desired TFTP block size
  46. ; For Ethernet MTU is normally 1500.  Unfortunately there seems to
  47. ; be a fair number of networks with "substandard" MTUs which break.
  48. ; The code assumes TFTP_LARGEBLK <= 2K.
  49. TFTP_MTU    equ 1472
  50. TFTP_LARGEBLK    equ (TFTP_MTU-20-8-4)    ; MTU - IP hdr - UDP hdr - TFTP hdr
  51. ; Standard TFTP block size
  52. TFTP_BLOCKSIZE_LG2 equ 9        ; log2(bytes/block)
  53. TFTP_BLOCKSIZE    equ (1 << TFTP_BLOCKSIZE_LG2)
  54. %assign USE_PXE_PROVIDED_STACK 1    ; Use stack provided by PXE?
  55.  
  56. SECTOR_SHIFT    equ TFTP_BLOCKSIZE_LG2
  57. SECTOR_SIZE    equ TFTP_BLOCKSIZE
  58.  
  59. ;
  60. ; This is what we need to do when idle
  61. ;
  62. %macro    RESET_IDLE 0
  63.     call reset_idle
  64. %endmacro
  65. %macro    DO_IDLE 0
  66.     call check_for_arp
  67. %endmacro
  68.  
  69. ;
  70. ; TFTP operation codes
  71. ;
  72. TFTP_RRQ    equ htons(1)        ; Read request
  73. TFTP_WRQ    equ htons(2)        ; Write request
  74. TFTP_DATA    equ htons(3)        ; Data packet
  75. TFTP_ACK    equ htons(4)        ; ACK packet
  76. TFTP_ERROR    equ htons(5)        ; ERROR packet
  77. TFTP_OACK    equ htons(6)        ; OACK packet
  78.  
  79. ;
  80. ; TFTP error codes
  81. ;
  82. TFTP_EUNDEF    equ htons(0)        ; Unspecified error
  83. TFTP_ENOTFOUND    equ htons(1)        ; File not found
  84. TFTP_EACCESS    equ htons(2)        ; Access violation
  85. TFTP_ENOSPACE    equ htons(3)        ; Disk full
  86. TFTP_EBADOP    equ htons(4)        ; Invalid TFTP operation
  87. TFTP_EBADID    equ htons(5)        ; Unknown transfer
  88. TFTP_EEXISTS    equ htons(6)        ; File exists
  89. TFTP_ENOUSER    equ htons(7)        ; No such user
  90. TFTP_EOPTNEG    equ htons(8)        ; Option negotiation failure
  91.  
  92. ;
  93. ; The following structure is used for "virtual kernels"; i.e. LILO-style
  94. ; option labels.  The options we permit here are `kernel' and `append
  95. ; Since there is no room in the bottom 64K for all of these, we
  96. ; stick them at vk_seg:0000 and copy them down before we need them.
  97. ;
  98.         struc vkernel
  99. vk_vname:    resb FILENAME_MAX    ; Virtual name **MUST BE FIRST!**
  100. vk_rname:    resb FILENAME_MAX    ; Real name
  101. vk_ipappend:    resb 1            ; "IPAPPEND" flag
  102.         resb 1            ; Pad
  103. vk_appendlen:    resw 1
  104.         alignb 4
  105. vk_append:    resb max_cmd_len+1    ; Command line
  106.         alignb 4
  107. vk_end:        equ $            ; Should be <= vk_size
  108.         endstruc
  109.  
  110. ;
  111. ; Segment assignments in the bottom 640K
  112. ; 0000h - main code/data segment (and BIOS segment)
  113. ;
  114. real_mode_seg    equ 4000h
  115. vk_seg          equ 3000h        ; Virtual kernels
  116. xfer_buf_seg    equ 2000h        ; Bounce buffer for I/O to high mem
  117. pktbuf_seg    equ 1000h        ; Packet buffers segments
  118. comboot_seg    equ real_mode_seg    ; COMBOOT image loading zone
  119.  
  120. ;
  121. ; BOOTP/DHCP packet pattern
  122. ;
  123.         struc bootp_t        
  124. bootp:
  125. .opcode        resb 1            ; BOOTP/DHCP "opcode"
  126. .hardware    resb 1            ; ARP hardware type
  127. .hardlen    resb 1            ; Hardware address length
  128. .gatehops    resb 1            ; Used by forwarders
  129. .ident        resd 1            ; Transaction ID
  130. .seconds    resw 1            ; Seconds elapsed
  131. .flags        resw 1            ; Broadcast flags
  132. .cip        resd 1            ; Client IP
  133. .yip        resd 1            ; "Your" IP
  134. .sip        resd 1            ; Next server IP
  135. .gip        resd 1            ; Relay agent IP
  136. .macaddr    resb 16            ; Client MAC address
  137. .sname        resb 64            ; Server name (optional)
  138. .bootfile    resb 128        ; Boot file name
  139. .option_magic    resd 1            ; Vendor option magic cookie
  140. .options    resb 1260        ; Vendor options
  141.         endstruc    
  142.  
  143. BOOTP_OPTION_MAGIC    equ htonl(0x63825363)    ; See RFC 2132
  144.  
  145. ;
  146. ; TFTP connection data structure.  Each one of these corresponds to a local
  147. ; UDP port.  The size of this structure must be a power of 2.
  148. ; HBO = host byte order; NBO = network byte order
  149. ; (*) = written by options negotiation code, must be dword sized
  150. ;
  151.         struc open_file_t
  152. tftp_localport    resw 1            ; Local port number    (0 = not in use)
  153. tftp_remoteport    resw 1            ; Remote port number
  154. tftp_remoteip    resd 1            ; Remote IP address
  155. tftp_filepos    resd 1            ; Bytes downloaded (including buffer)
  156. tftp_filesize    resd 1            ; Total file size(*)
  157. tftp_blksize    resd 1            ; Block size for this connection(*)
  158. tftp_bytesleft    resw 1            ; Unclaimed data bytes
  159. tftp_lastpkt    resw 1            ; Sequence number of last packet (NBO)
  160. tftp_dataptr    resw 1            ; Pointer to available data
  161.         resw 2            ; Currently unusued
  162.         ; At end since it should not be zeroed on socked close
  163. tftp_pktbuf    resw 1            ; Packet buffer offset
  164.         endstruc
  165. %ifndef DEPEND
  166. %if (open_file_t_size & (open_file_t_size-1))
  167. %error "open_file_t is not a power of 2"
  168. %endif
  169. %endif
  170.  
  171. ; ---------------------------------------------------------------------------
  172. ;   BEGIN CODE
  173. ; ---------------------------------------------------------------------------
  174.  
  175. ;
  176. ; Memory below this point is reserved for the BIOS and the MBR
  177. ;
  178.          section .earlybss
  179. trackbufsize    equ 8192
  180. trackbuf    resb trackbufsize    ; Track buffer goes here
  181. getcbuf        resb trackbufsize
  182.         ; ends at 4800h
  183.  
  184.         ; Put some large buffers here, before RBFG_brainfuck,
  185.         ; where we can still carefully control the address
  186.         ; assignments...
  187.  
  188.         alignb open_file_t_size
  189. Files        resb MAX_OPEN*open_file_t_size
  190.  
  191.         alignb FILENAME_MAX
  192. BootFile    resb 256        ; Boot file from DHCP packet
  193. ConfigServer    resd 1            ; Null prefix for mangled config name
  194. ConfigName    resb 256-4        ; Configuration file from DHCP option
  195. PathPrefix    resb 256        ; Path prefix derived from boot file
  196. DotQuadBuf    resb 16            ; Buffer for dotted-quad IP address
  197. IPOption    resb 80            ; ip= option buffer
  198. InitStack    resd 1            ; Pointer to reset stack
  199.  
  200. ; Warning here: RBFG build 22 randomly overwrites memory location
  201. ; [0x5680,0x576c), possibly more.  It seems that it gets confused and
  202. ; screws up the pointer to its own internal packet buffer and starts
  203. ; writing a received ARP packet into low memory.
  204. RBFG_brainfuck    resb 0E00h
  205.  
  206.         section .bss
  207.         alignb 4
  208. RebootTime    resd 1            ; Reboot timeout, if set by option
  209. StrucPtr    resd 1            ; Pointer to PXENV+ or !PXE structure
  210. APIVer        resw 1            ; PXE API version found
  211. IPOptionLen    resw 1            ; Length of IPOption
  212. IdleTimer    resw 1            ; Time to check for ARP?
  213. LocalBootType    resw 1            ; Local boot return code
  214. PktTimeout    resw 1            ; Timeout for current packet
  215. RealBaseMem    resw 1            ; Amount of DOS memory after freeing
  216. OverLoad    resb 1            ; Set if DHCP packet uses "overloading"
  217.  
  218. ; The relative position of these fields matter!
  219. MACLen        resb 1            ; MAC address len
  220. MACType        resb 1            ; MAC address type
  221. MAC        resb 16            ; Actual MAC address
  222. BOOTIFStr    resb 7            ; Space for "BOOTIF="
  223. MACStr        resb 3*17        ; MAC address as a string
  224.  
  225. ;
  226. ; PXE packets which don't need static initialization
  227. ;
  228.         alignb 4
  229. pxe_unload_stack_pkt:
  230. .status:    resw 1            ; Status
  231. .reserved:    resw 10            ; Reserved
  232. pxe_unload_stack_pkt_len    equ $-pxe_unload_stack_pkt
  233.  
  234.         alignb 16
  235.         ; BOOTP/DHCP packet buffer
  236.  
  237.         alignb 16
  238. packet_buf    resb 2048        ; Transfer packet
  239. packet_buf_size    equ $-packet_buf
  240.  
  241. ;
  242. ; Constants for the xfer_buf_seg
  243. ;
  244. ; The xfer_buf_seg is also used to store message file buffers.  We
  245. ; need two trackbuffers (text and graphics), plus a work buffer
  246. ; for the graphics decompressor.
  247. ;
  248. xbs_textbuf    equ 0            ; Also hard-coded, do not change
  249. xbs_vgabuf    equ trackbufsize
  250. xbs_vgatmpbuf    equ 2*trackbufsize
  251.  
  252.         section .text
  253.         ;
  254.         ; PXELINUX needs more BSS than the other derivatives;
  255.         ; therefore we relocate it from 7C00h on startup.
  256.         ;
  257. StackBuf    equ $            ; Base of stack if we use our own
  258.  
  259. ;
  260. ; Primary entry point.
  261. ;
  262. bootsec        equ $
  263. _start:
  264.         pushfd            ; Paranoia... in case of return to PXE
  265.         pushad            ; ... save as much state as possible
  266.         push ds
  267.         push es
  268.         push fs
  269.         push gs
  270.  
  271.         xor ax,ax
  272.         mov ds,ax
  273.         mov es,ax    
  274.  
  275.         ; This is uglier than it should be, but works around
  276.         ; some NASM 0.98.38 bugs.
  277.         mov di,section..bcopy32.start
  278.         add di,__bcopy_size-4
  279.         lea si,[di-(TEXT_START-7C00h)]
  280.         lea cx,[di-(TEXT_START-4)]
  281.         shr cx,2
  282.         std            ; Overlapping areas, copy backwards
  283.         rep movsd
  284.  
  285.         jmp 0:_start1        ; Canonicalize address
  286. _start1:
  287.         mov bp,sp
  288.         les bx,[bp+48]        ; ES:BX -> !PXE or PXENV+ structure
  289.  
  290.         ; That is all pushed onto the PXE stack.  Save the pointer
  291.         ; to it and switch to an internal stack.
  292.         mov [InitStack],sp
  293.         mov [InitStack+2],ss
  294.  
  295. %if USE_PXE_PROVIDED_STACK
  296.         ; Apparently some platforms go bonkers if we
  297.         ; set up our own stack...
  298.         mov [BaseStack],sp
  299.         mov [BaseStack+4],ss
  300. %endif
  301.  
  302.         cli            ; Paranoia
  303.         lss esp,[BaseStack]
  304.  
  305.         sti            ; Stack set up and ready
  306.         cld            ; Copy upwards
  307.  
  308. ;
  309. ; Initialize screen (if we're using one)
  310. ;
  311.         ; Now set up screen parameters
  312.         call adjust_screen
  313.  
  314.         ; Wipe the F-key area
  315.         mov al,NULLFILE
  316.         mov di,FKeyName
  317.         mov cx,10*(1 << FILENAME_MAX_LG2)
  318.         push es            ; Save ES -> PXE structure
  319.         push ds            ; ES <- DS
  320.         pop es
  321.         rep stosb
  322.         pop es            ; Restore ES
  323.  
  324. ;
  325. ; Tell the user we got this far
  326. ;
  327.         mov si,syslinux_banner
  328.         call writestr
  329.  
  330.         mov si,copyright_str
  331.         call writestr
  332.  
  333. ;
  334. ; Assume API version 2.1, in case we find the !PXE structure without
  335. ; finding the PXENV+ structure.  This should really look at the Base
  336. ; Code ROM ID structure in have_pxe, but this is adequate for now --
  337. ; if we have !PXE, we have to be 2.1 or higher, and we don't care
  338. ; about higher versions than that.
  339. ;
  340.         mov word [APIVer],0201h
  341.  
  342. ;
  343. ; Now we need to find the !PXE structure.  It's *supposed* to be pointed
  344. ; to by SS:[SP+4], but support INT 1Ah, AX=5650h method as well.
  345. ; FIX: ES:BX should point to the PXENV+ structure on entry as well.
  346. ; We should make that the second test, and not trash ES:BX...
  347.         cmp dword [es:bx], '!PXE'
  348.         je have_pxe
  349.  
  350.         ; Uh-oh, not there... try plan B
  351.         mov ax, 5650h
  352.         int 1Ah                    ; May trash regs
  353.         jc no_pxe
  354.         cmp ax,564Eh
  355.         jne no_pxe
  356.  
  357.         ; Okay, that gave us the PXENV+ structure, find !PXE
  358.         ; structure from that (if available)
  359.         cmp dword [es:bx], 'PXEN'
  360.         jne no_pxe
  361.         cmp word [es:bx+4], 'V+'
  362.         je have_pxenv
  363.  
  364.         ; Nothing there either.  Last-ditch: scan memory
  365.         call memory_scan_for_pxe_struct        ; !PXE scan
  366.         jnc have_pxe
  367.         call memory_scan_for_pxenv_struct    ; PXENV+ scan
  368.         jnc have_pxenv
  369.  
  370. no_pxe:        mov si,err_nopxe
  371.         call writestr
  372.         jmp kaboom
  373.  
  374. have_pxenv:
  375.         mov [StrucPtr],bx
  376.         mov [StrucPtr+2],es
  377.  
  378.         mov si,found_pxenv
  379.         call writestr
  380.  
  381.         mov si,apiver_str
  382.         call writestr
  383.         mov ax,[es:bx+6]
  384.         mov [APIVer],ax
  385.         call writehex4
  386.         call crlf
  387.  
  388.         cmp ax,0201h            ; API version 2.1 or higher
  389.         jb old_api
  390.         mov si,bx
  391.         mov ax,es
  392.         les bx,[es:bx+28h]        ; !PXE structure pointer
  393.         cmp dword [es:bx],'!PXE'
  394.         je have_pxe
  395.  
  396.         ; Nope, !PXE structure missing despite API 2.1+, or at least
  397.         ; the pointer is missing.  Do a last-ditch attempt to find it.
  398.         call memory_scan_for_pxe_struct
  399.         jnc have_pxe
  400.  
  401.         ; Otherwise, no dice, use PXENV+ structure
  402.         mov bx,si
  403.         mov es,ax
  404.  
  405. old_api:    ; Need to use a PXENV+ structure
  406.         mov si,using_pxenv_msg
  407.         call writestr
  408.  
  409.         mov eax,[es:bx+0Ah]        ; PXE RM API
  410.         mov [PXENVEntry],eax
  411.  
  412.         mov si,undi_data_msg
  413.         call writestr
  414.         mov ax,[es:bx+20h]
  415.         call writehex4
  416.         call crlf
  417.         mov si,undi_data_len_msg
  418.         call writestr
  419.         mov ax,[es:bx+22h]
  420.         call writehex4
  421.         call crlf
  422.         mov si,undi_code_msg
  423.         call writestr
  424.         mov ax,[es:bx+24h]
  425.         call writehex4
  426.         call crlf
  427.         mov si,undi_code_len_msg
  428.         call writestr
  429.         mov ax,[es:bx+26h]
  430.         call writehex4
  431.         call crlf
  432.  
  433.         ; Compute base memory size from PXENV+ structure
  434.         xor esi,esi
  435.         movzx eax,word [es:bx+20h]    ; UNDI data seg
  436.         cmp ax,[es:bx+24h]        ; UNDI code seg
  437.         ja .use_data
  438.         mov ax,[es:bx+24h]
  439.         mov si,[es:bx+26h]
  440.         jmp short .combine
  441. .use_data:
  442.         mov si,[es:bx+22h]
  443. .combine:
  444.         shl eax,4
  445.         add eax,esi
  446.         shr eax,10            ; Convert to kilobytes
  447.         mov [RealBaseMem],ax
  448.  
  449.         mov si,pxenventry_msg
  450.         call writestr
  451.         mov ax,[PXENVEntry+2]
  452.         call writehex4
  453.         mov al,':'
  454.         call writechr
  455.         mov ax,[PXENVEntry]
  456.         call writehex4
  457.         call crlf
  458.         jmp have_entrypoint
  459.  
  460. have_pxe:
  461.         mov [StrucPtr],bx
  462.         mov [StrucPtr+2],es
  463.  
  464.         mov eax,[es:bx+10h]
  465.         mov [PXEEntry],eax
  466.  
  467.         mov si,undi_data_msg
  468.         call writestr
  469.         mov eax,[es:bx+2Ah]
  470.         call writehex8
  471.         call crlf
  472.         mov si,undi_data_len_msg
  473.         call writestr
  474.         mov ax,[es:bx+2Eh]
  475.         call writehex4
  476.         call crlf
  477.         mov si,undi_code_msg
  478.         call writestr
  479.         mov ax,[es:bx+32h]
  480.         call writehex8
  481.         call crlf
  482.         mov si,undi_code_len_msg
  483.         call writestr
  484.         mov ax,[es:bx+36h]
  485.         call writehex4
  486.         call crlf
  487.  
  488.         ; Compute base memory size from !PXE structure
  489.         xor esi,esi
  490.         mov eax,[es:bx+2Ah]
  491.         cmp eax,[es:bx+32h]
  492.         ja .use_data
  493.         mov eax,[es:bx+32h]
  494.         mov si,[es:bx+36h]
  495.         jmp short .combine
  496. .use_data:
  497.         mov si,[es:bx+2Eh]
  498. .combine:
  499.         add eax,esi
  500.         shr eax,10
  501.         mov [RealBaseMem],ax
  502.  
  503.         mov si,pxeentry_msg
  504.         call writestr
  505.         mov ax,[PXEEntry+2]
  506.         call writehex4
  507.         mov al,':'
  508.         call writechr
  509.         mov ax,[PXEEntry]
  510.         call writehex4
  511.         call crlf
  512.  
  513. have_entrypoint:
  514.         push cs
  515.         pop es                ; Restore CS == DS == ES
  516.  
  517. ;
  518. ; Network-specific initialization
  519. ;
  520.         xor ax,ax
  521.         mov [LocalDomain],al        ; No LocalDomain received
  522.  
  523. ;
  524. ; Now attempt to get the BOOTP/DHCP packet that brought us life (and an IP
  525. ; address).  This lives in the DHCPACK packet (query info 2).
  526. ;
  527. query_bootp:
  528.         mov di,pxe_bootp_query_pkt_2
  529.         mov bx,PXENV_GET_CACHED_INFO
  530.  
  531.         call pxenv
  532.         push word [pxe_bootp_query_pkt_2.status]
  533.         jc .pxe_err1
  534.         cmp ax,byte 0
  535.         je .pxe_ok
  536. .pxe_err1:
  537.         mov di,pxe_bootp_size_query_pkt
  538.         mov bx,PXENV_GET_CACHED_INFO
  539.  
  540.         call pxenv
  541.         jc .pxe_err
  542. .pxe_size:
  543.         mov ax,[pxe_bootp_size_query_pkt.buffersize]
  544.         call writehex4
  545.         call crlf
  546.  
  547. .pxe_err:
  548.         mov si,err_pxefailed
  549.         call writestr
  550.         call writehex4
  551.         mov al, ' '
  552.         call writechr
  553.         pop ax                ; Status
  554.         call writehex4
  555.         call crlf
  556.         jmp kaboom            ; We're dead
  557.  
  558. .pxe_ok:
  559.         pop cx                ; Forget status
  560.         mov cx,[pxe_bootp_query_pkt_2.buffersize]
  561.         call parse_dhcp            ; Parse DHCP packet
  562. ;
  563. ; Save away MAC address (assume this is in query info 2.  If this
  564. ; turns out to be problematic it might be better getting it from
  565. ; the query info 1 packet.)
  566. ;
  567. .save_mac:
  568.         movzx cx,byte [trackbuf+bootp.hardlen]
  569.         mov [MACLen],cl
  570.         mov al,[trackbuf+bootp.hardware]
  571.         mov [MACType],al
  572.         mov si,trackbuf+bootp.macaddr
  573.         mov di,MAC
  574.         push cx
  575.         rep movsb
  576.         mov cx,MAC+16
  577.         sub cx,di
  578.         xor ax,ax
  579.         rep stosb
  580.         
  581.         mov si,bootif_str
  582.         mov di,BOOTIFStr
  583.         mov cx,bootif_str_len
  584.         rep movsb
  585.     
  586.         pop cx
  587.         mov si,MACType
  588.         inc cx
  589.         mov bx,hextbl_lower
  590. .hexify_mac:
  591.         lodsb
  592.         mov ah,al
  593.         shr al,4
  594.         xlatb
  595.         stosb
  596.         mov al,ah
  597.         and al,0Fh
  598.         xlatb
  599.         stosb
  600.         mov al,'-'
  601.         stosb
  602.         loop .hexify_mac
  603.         mov [di-1],byte 0        ; Null-terminate and strip final colon
  604.  
  605. ;
  606. ; Now, get the boot file and other info.  This lives in the CACHED_REPLY
  607. ; packet (query info 3).
  608. ;
  609.         mov [pxe_bootp_size_query_pkt.packettype], byte 3
  610.  
  611.         mov di,pxe_bootp_query_pkt_3
  612.         mov bx,PXENV_GET_CACHED_INFO
  613.  
  614.         call pxenv
  615.         push word [pxe_bootp_query_pkt_3.status]
  616.         jc .pxe_err1
  617.         cmp ax,byte 0
  618.         jne .pxe_err1
  619.  
  620.         ; Packet loaded OK...
  621.         pop cx                ; Forget status
  622.         mov cx,[pxe_bootp_query_pkt_3.buffersize]
  623.         call parse_dhcp            ; Parse DHCP packet
  624. ;
  625. ; Generate ip= option
  626. ;
  627.         call genipopt
  628.  
  629. ;
  630. ; Print IP address
  631. ;
  632.         mov eax,[MyIP]
  633.         mov di,DotQuadBuf
  634.         push di
  635.         call gendotquad            ; This takes network byte order input
  636.  
  637.         xchg ah,al            ; Convert to host byte order
  638.         ror eax,16            ; (BSWAP doesn't work on 386)
  639.         xchg ah,al
  640.  
  641.         mov si,myipaddr_msg
  642.         call writestr
  643.         call writehex8
  644.         mov al,' '
  645.         call writechr
  646.         pop si                ; DotQuadBuf
  647.         call writestr
  648.         call crlf
  649.  
  650.         mov si,IPOption
  651.         call writestr
  652.         call crlf
  653.  
  654. ;
  655. ; Check to see if we got any PXELINUX-specific DHCP options; in particular,
  656. ; if we didn't get the magic enable, do not recognize any other options.
  657. ;
  658. check_dhcp_magic:
  659.         test byte [DHCPMagic], 1    ; If we didn't get the magic enable...
  660.         jnz .got_magic
  661.         mov byte [DHCPMagic], 0        ; If not, kill all other options
  662. .got_magic:
  663.     
  664.  
  665. ;
  666. ; Initialize UDP stack
  667. ;
  668. udp_init:
  669.         mov eax,[MyIP]
  670.         mov [pxe_udp_open_pkt.sip],eax
  671.         mov di,pxe_udp_open_pkt
  672.         mov bx,PXENV_UDP_OPEN
  673.         call pxenv
  674.         jc .failed
  675.         cmp word [pxe_udp_open_pkt.status], byte 0
  676.         je .success
  677. .failed:    mov si,err_udpinit
  678.         call writestr
  679.         jmp kaboom
  680. .success:
  681.  
  682. ;
  683. ; Common initialization code
  684. ;
  685. %include "init.inc"
  686. %include "cpuinit.inc"
  687.  
  688. ;
  689. ; Now we're all set to start with our *real* business.    First load the
  690. ; configuration file (if any) and parse it.
  691. ;
  692. ; In previous versions I avoided using 32-bit registers because of a
  693. ; rumour some BIOSes clobbered the upper half of 32-bit registers at
  694. ; random.  I figure, though, that if there are any of those still left
  695. ; they probably won't be trying to install Linux on them...
  696. ;
  697. ; The code is still ripe with 16-bitisms, though.  Not worth the hassle
  698. ; to take'm out.  In fact, we may want to put them back if we're going
  699. ; to boot ELKS at some point.
  700. ;
  701.  
  702. ;
  703. ; Store standard filename prefix
  704. ;
  705. prefix:        test byte [DHCPMagic], 04h    ; Did we get a path prefix option
  706.         jnz .got_prefix
  707.         mov si,BootFile
  708.         mov di,PathPrefix
  709.         cld
  710.         call strcpy
  711.         mov cx,di
  712.         sub cx,PathPrefix+1
  713.         std
  714.         lea si,[di-2]            ; Skip final null!
  715. .find_alnum:    lodsb
  716.         or al,20h
  717.         cmp al,'.'            ; Count . or - as alphanum
  718.         je .alnum
  719.         cmp al,'-'
  720.         je .alnum
  721.         cmp al,'0'
  722.         jb .notalnum
  723.         cmp al,'9'
  724.         jbe .alnum
  725.         cmp al,'a'
  726.         jb .notalnum
  727.         cmp al,'z'
  728.         ja .notalnum
  729. .alnum:        loop .find_alnum
  730.         dec si
  731. .notalnum:    mov byte [si+2],0        ; Zero-terminate after delimiter
  732.         cld
  733. .got_prefix:
  734.         mov si,tftpprefix_msg
  735.         call writestr
  736.         mov si,PathPrefix
  737.         call writestr
  738.         call crlf
  739.  
  740. ;
  741. ; Load configuration file
  742. ;
  743. find_config:
  744.  
  745. ;
  746. ; Begin looking for configuration file
  747. ;
  748. config_scan:
  749.         mov di,ConfigServer
  750.         xor eax,eax
  751.         stosd                ; The config file is always from the server
  752.  
  753.         test byte [DHCPMagic], 02h
  754.         jz .no_option
  755.  
  756.         ; We got a DHCP option, try it first
  757.         mov si,trying_msg
  758.         call writestr
  759.         ; mov di,ConfigName        ; - already the case
  760.         mov si,di
  761.         call writestr
  762.         call crlf
  763.         mov di,ConfigServer
  764.         call open
  765.         jnz .success
  766.  
  767. .no_option:
  768.         mov di,ConfigName
  769.         mov si,cfgprefix
  770.         mov cx,cfgprefix_len
  771.         rep movsb
  772.  
  773.         ; Try loading by MAC address
  774.         ; Have to guess config file name
  775.         push di
  776.         mov si,MACStr
  777.         mov cx,(3*17+1)/2
  778.         rep movsw
  779.         mov si,trying_msg
  780.         call writestr
  781.         mov di,ConfigName
  782.         mov si,di
  783.         call writestr
  784.         call crlf
  785.         mov di,ConfigServer
  786.         call open
  787.         pop di
  788.         jnz .success
  789.  
  790. .scan_ip:
  791.         mov cx,8
  792.         mov eax,[MyIP]
  793.         xchg ah,al            ; Convert to host byte order
  794.         ror eax,16
  795.         xchg ah,al
  796. .hexify_loop:    rol eax,4
  797.         push eax
  798.         and al,0Fh
  799.         cmp al,10
  800.         jae .high
  801. .low:        add al,'0'
  802.         jmp short .char
  803. .high:        add al,'A'-10
  804. .char:        stosb
  805.         pop eax
  806.         loop .hexify_loop
  807.  
  808.         mov cx,9            ; Up to 9 attempts
  809.  
  810. .tryagain:    mov byte [di],0
  811.         cmp cx,byte 1
  812.         jne .not_default
  813.         pusha
  814.         mov si,default_str
  815.         mov cx,default_len
  816.         rep movsb            ; Copy "default" string
  817.         popa
  818. .not_default:    pusha
  819.         mov si,trying_msg
  820.         call writestr
  821.         mov di,ConfigName
  822.         mov si,di
  823.         call writestr
  824.         call crlf
  825.         mov di,ConfigServer
  826.         call open
  827.         popa
  828.         jnz .success
  829.         dec di
  830.         loop .tryagain
  831.  
  832.         jmp no_config_file
  833.  
  834. .success:
  835.  
  836. ;
  837. ; Now we have the config file open.  Parse the config file and
  838. ; run the user interface.
  839. ;
  840. %include "ui.inc"
  841.  
  842. ;
  843. ; Linux kernel loading code is common.  However, we need to define
  844. ; a couple of helper macros...
  845. ;
  846.  
  847. ; Handle "ipappend" option
  848. %define HAVE_SPECIAL_APPEND
  849. %macro    SPECIAL_APPEND 0
  850.         test byte [IPAppend],01h    ; ip=
  851.         jz .noipappend1
  852.         mov si,IPOption
  853.         mov cx,[IPOptionLen]
  854.         rep movsb
  855.         mov al,' '
  856.         stosb
  857. .noipappend1:
  858.         test byte [IPAppend],02h
  859.         jz .noipappend2
  860.         mov si,BOOTIFStr
  861.         call strcpy
  862.         mov byte [es:di-1],' '        ; Replace null with space
  863. .noipappend2:
  864. %endmacro
  865.  
  866. ; Unload PXE stack
  867. %define HAVE_UNLOAD_PREP
  868. %macro    UNLOAD_PREP 0
  869.         call unload_pxe
  870. %endmacro
  871.  
  872. %include "runkernel.inc"
  873.  
  874. ;
  875. ; COMBOOT-loading code
  876. ;
  877. %include "comboot.inc"
  878. %include "com32.inc"
  879. %include "cmdline.inc"
  880.  
  881. ;
  882. ; Boot sector loading code
  883. ;
  884. %include "bootsect.inc"
  885.  
  886. ;
  887. ; Boot to the local disk by returning the appropriate PXE magic.
  888. ; AX contains the appropriate return code.
  889. ;
  890. local_boot:
  891.         mov si,cs
  892.         mov ds,si            ; Restore DI
  893.         lss esp,[BaseStack]
  894.         mov [LocalBootType],ax
  895.         call vgaclearmode
  896.         mov si,localboot_msg
  897.         call writestr
  898.         ; Restore the environment we were called with
  899.         lss sp,[InitStack]
  900.         pop gs
  901.         pop fs
  902.         pop es
  903.         pop ds
  904.         popad
  905.         mov ax,[cs:LocalBootType]
  906.         popfd
  907.         retf                ; Return to PXE
  908.  
  909. ;
  910. ; abort_check: let the user abort with <ESC> or <Ctrl-C>
  911. ;
  912. abort_check:
  913.         call pollchar
  914.         jz ac_ret1
  915.         pusha
  916.         call getchar
  917.         cmp al,27            ; <ESC>
  918.         je ac_kill
  919.         cmp al,3            ; <Ctrl-C>
  920.         jne ac_ret2
  921. ac_kill:    mov si,aborted_msg
  922.  
  923. ;
  924. ; abort_load: Called by various routines which wants to print a fatal
  925. ;             error message and return to the command prompt.  Since this
  926. ;             may happen at just about any stage of the boot process, assume
  927. ;             our state is messed up, and just reset the segment registers
  928. ;             and the stack forcibly.
  929. ;
  930. ;             SI    = offset (in _text) of error message to print
  931. ;
  932. abort_load:
  933.                 mov ax,cs                       ; Restore CS = DS = ES
  934.                 mov ds,ax
  935.                 mov es,ax
  936.         lss esp,[BaseStack]
  937.         sti
  938.                 call cwritestr                  ; Expects SI -> error msg
  939. al_ok:          jmp enter_command               ; Return to command prompt
  940. ;
  941. ; End of abort_check
  942. ;
  943. ac_ret2:    popa
  944. ac_ret1:    ret
  945.  
  946.  
  947. ;
  948. ; kaboom: write a message and bail out.  Wait for quite a while,
  949. ;      or a user keypress, then do a hard reboot.
  950. ;
  951. kaboom:
  952.         mov ax,cs
  953.         mov es,ax
  954.         mov ds,ax
  955.         lss esp,[BaseStack]
  956.         sti
  957. .patch:        mov si,bailmsg
  958.         call writestr        ; Returns with AL = 0
  959. .drain:        call pollchar
  960.         jz .drained
  961.         call getchar
  962.         jmp short .drain
  963. .drained:
  964.         mov edi,[RebootTime]
  965.         mov al,[DHCPMagic]
  966.         and al,09h        ; Magic+Timeout
  967.         cmp al,09h
  968.         je .time_set
  969.         mov edi,REBOOT_TIME
  970. .time_set:
  971.         mov cx,18
  972. .wait1:        push cx
  973.         mov ecx,edi
  974. .wait2:        mov dx,[BIOS_timer]
  975. .wait3:        call pollchar
  976.         jnz .keypress
  977.         cmp dx,[BIOS_timer]
  978.         je .wait3
  979.         loop .wait2,ecx
  980.         mov al,'.'
  981.         call writechr
  982.         pop cx
  983.         loop .wait1
  984. .keypress:
  985.         call crlf
  986.         mov word [BIOS_magic],0    ; Cold reboot
  987.         jmp 0F000h:0FFF0h    ; Reset vector address
  988.  
  989. ;
  990. ; memory_scan_for_pxe_struct:
  991. ;
  992. ;    If none of the standard methods find the !PXE structure, look for it
  993. ;    by scanning memory.
  994. ;
  995. ;    On exit, if found:
  996. ;        CF = 0, ES:BX -> !PXE structure
  997. ;    Otherwise CF = 1, all registers saved
  998. ;    
  999. memory_scan_for_pxe_struct:
  1000.         push ds
  1001.         pusha
  1002.         mov ax,cs
  1003.         mov ds,ax
  1004.         mov si,trymempxe_msg
  1005.         call writestr
  1006.         mov ax,[BIOS_fbm]    ; Starting segment
  1007.         shl ax,(10-4)        ; Kilobytes -> paragraphs
  1008. ;        mov ax,01000h        ; Start to look here
  1009.         dec ax            ; To skip inc ax
  1010. .mismatch:
  1011.         inc ax
  1012.         cmp ax,0A000h        ; End of memory
  1013.         jae .not_found
  1014.         call writehex4
  1015.         mov si,fourbs_msg
  1016.         call writestr
  1017.         mov es,ax
  1018.         mov edx,[es:0]
  1019.         cmp edx,'!PXE'
  1020.         jne .mismatch
  1021.         movzx cx,byte [es:4]    ; Length of structure
  1022.         cmp cl,08h        ; Minimum length
  1023.         jb .mismatch
  1024.         push ax
  1025.         xor ax,ax
  1026.         xor si,si
  1027. .checksum:    es lodsb
  1028.         add ah,al
  1029.         loop .checksum
  1030.         pop ax
  1031.         jnz .mismatch        ; Checksum must == 0
  1032. .found:        mov bp,sp
  1033.         xor bx,bx
  1034.         mov [bp+8],bx        ; Save BX into stack frame (will be == 0)
  1035.         mov ax,es
  1036.         call writehex4
  1037.         call crlf
  1038.         popa
  1039.         pop ds
  1040.         clc
  1041.         ret
  1042. .not_found:    mov si,notfound_msg
  1043.         call writestr
  1044.         popa
  1045.         pop ds
  1046.         stc
  1047.         ret
  1048.  
  1049. ;
  1050. ; memory_scan_for_pxenv_struct:
  1051. ;
  1052. ;    If none of the standard methods find the PXENV+ structure, look for it
  1053. ;    by scanning memory.
  1054. ;
  1055. ;    On exit, if found:
  1056. ;        CF = 0, ES:BX -> PXENV+ structure
  1057. ;    Otherwise CF = 1, all registers saved
  1058. ;    
  1059. memory_scan_for_pxenv_struct:
  1060.         pusha
  1061.         mov si,trymempxenv_msg
  1062.         call writestr
  1063. ;        mov ax,[BIOS_fbm]    ; Starting segment
  1064. ;        shl ax,(10-4)        ; Kilobytes -> paragraphs
  1065.         mov ax,01000h        ; Start to look here
  1066.         dec ax            ; To skip inc ax
  1067. .mismatch:
  1068.         inc ax
  1069.         cmp ax,0A000h        ; End of memory
  1070.         jae .not_found
  1071.         mov es,ax
  1072.         mov edx,[es:0]
  1073.         cmp edx,'PXEN'
  1074.         jne .mismatch
  1075.         mov dx,[es:4]
  1076.         cmp dx,'V+'
  1077.         jne .mismatch
  1078.         movzx cx,byte [es:8]    ; Length of structure
  1079.         cmp cl,26h        ; Minimum length
  1080.         jb .mismatch
  1081.         xor ax,ax
  1082.         xor si,si
  1083. .checksum:    es lodsb
  1084.         add ah,al
  1085.         loop .checksum
  1086.         and ah,ah
  1087.         jnz .mismatch        ; Checksum must == 0
  1088. .found:        mov bp,sp
  1089.         mov [bp+8],bx        ; Save BX into stack frame
  1090.         mov ax,bx
  1091.         call writehex4
  1092.         call crlf
  1093.         clc
  1094.         ret
  1095. .not_found:    mov si,notfound_msg
  1096.         call writestr
  1097.         popad
  1098.         stc
  1099.         ret
  1100.  
  1101. ;
  1102. ; searchdir:
  1103. ;
  1104. ;    Open a TFTP connection to the server 
  1105. ;
  1106. ;         On entry:
  1107. ;        DS:DI    = mangled filename
  1108. ;         If successful:
  1109. ;        ZF clear
  1110. ;        SI    = socket pointer
  1111. ;        DX:AX    = file length in bytes
  1112. ;         If unsuccessful
  1113. ;        ZF set
  1114. ;
  1115.  
  1116. searchdir:
  1117.         push es
  1118.         mov ax,ds
  1119.         mov es,ax
  1120.         mov si,di
  1121.         push bp
  1122.         mov bp,sp
  1123.  
  1124.         call allocate_socket
  1125.         jz .error
  1126.  
  1127.         mov ax,PKT_RETRY    ; Retry counter
  1128.         mov word [PktTimeout],PKT_TIMEOUT    ; Initial timeout
  1129.     
  1130. .sendreq:    push ax            ; [bp-2]  - Retry counter
  1131.         push si            ; [bp-4]  - File name 
  1132.  
  1133.         mov di,packet_buf
  1134.         mov [pxe_udp_write_pkt.buffer],di
  1135.  
  1136.         mov ax,TFTP_RRQ        ; TFTP opcode
  1137.         stosw
  1138.  
  1139.         lodsd            ; EAX <- server override (if any)
  1140.         and eax,eax
  1141.         jnz .noprefix        ; No prefix, and we have the server
  1142.  
  1143.         push si            ; Add common prefix
  1144.         mov si,PathPrefix
  1145.         call strcpy
  1146.         dec di
  1147.         pop si
  1148.  
  1149.         mov eax,[ServerIP]    ; Get default server
  1150.  
  1151. .noprefix:
  1152.         call strcpy        ; Filename
  1153.  
  1154.         mov [bx+tftp_remoteip],eax
  1155.  
  1156.         push bx            ; [bp-6]  - TFTP block
  1157.         mov bx,[bx]
  1158.         push bx            ; [bp-8]  - TID (local port no)
  1159.  
  1160.         mov [pxe_udp_write_pkt.status],byte 0
  1161.         mov [pxe_udp_write_pkt.sip],eax
  1162.         ; Now figure out the gateway
  1163.         xor eax,[MyIP]
  1164.         and eax,[Netmask]
  1165.         jz .nogwneeded
  1166.         mov eax,[Gateway]
  1167. .nogwneeded:
  1168.         mov [pxe_udp_write_pkt.gip],eax
  1169.         mov [pxe_udp_write_pkt.lport],bx
  1170.         mov ax,[ServerPort]
  1171.         mov [pxe_udp_write_pkt.rport],ax
  1172.         mov si,tftp_tail
  1173.         mov cx,tftp_tail_len
  1174.         rep movsb
  1175.         sub di,packet_buf    ; Get packet size
  1176.         mov [pxe_udp_write_pkt.buffersize],di
  1177.  
  1178.         mov di,pxe_udp_write_pkt
  1179.         mov bx,PXENV_UDP_WRITE
  1180.         call pxenv
  1181.         jc .failure
  1182.         cmp word [pxe_udp_write_pkt.status],byte 0
  1183.         jne .failure
  1184.  
  1185.         ;
  1186.         ; Danger, Will Robinson!  We need to support timeout
  1187.         ; and retry lest we just lost a packet...
  1188.         ;
  1189.  
  1190.         ; Packet transmitted OK, now we need to receive
  1191. .getpacket:    push word [PktTimeout]    ; [bp-10]
  1192.         push word [BIOS_timer]    ; [bp-12]
  1193.  
  1194. .pkt_loop:    mov bx,[bp-8]        ; TID
  1195.         mov di,packet_buf
  1196.         mov word [pxe_udp_read_pkt.status],0
  1197.         mov [pxe_udp_read_pkt.buffer],di
  1198.         mov [pxe_udp_read_pkt.buffer+2],ds
  1199.         mov word [pxe_udp_read_pkt.buffersize],packet_buf_size
  1200.         mov eax,[MyIP]
  1201.         mov [pxe_udp_read_pkt.dip],eax
  1202.         mov [pxe_udp_read_pkt.lport],bx
  1203.         mov di,pxe_udp_read_pkt
  1204.         mov bx,PXENV_UDP_READ
  1205.         call pxenv
  1206.         and ax,ax
  1207.         jz .got_packet            ; Wait for packet
  1208. .no_packet:
  1209.         mov dx,[BIOS_timer]
  1210.         cmp dx,[bp-12]
  1211.         je .pkt_loop
  1212.         mov [bp-12],dx
  1213.         dec word [bp-10]        ; Timeout
  1214.         jnz .pkt_loop
  1215.         pop ax    ; Adjust stack
  1216.         pop ax
  1217.         shl word [PktTimeout],1        ; Exponential backoff
  1218.         jmp .failure
  1219.         
  1220. .got_packet:
  1221.         mov si,[bp-6]            ; TFTP pointer
  1222.         mov bx,[bp-8]            ; TID
  1223.  
  1224.         mov eax,[si+tftp_remoteip]
  1225.         cmp [pxe_udp_read_pkt.sip],eax    ; This is technically not to the TFTP spec?
  1226.         jne .no_packet
  1227.  
  1228.         ; Got packet - reset timeout
  1229.         mov word [PktTimeout],PKT_TIMEOUT
  1230.  
  1231.         pop ax    ; Adjust stack
  1232.         pop ax
  1233.  
  1234.         mov ax,[pxe_udp_read_pkt.rport]
  1235.         mov [si+tftp_remoteport],ax
  1236.  
  1237.         ; filesize <- -1 == unknown
  1238.         mov dword [si+tftp_filesize], -1
  1239.         ; Default blksize unless blksize option negotiated
  1240.         mov word [si+tftp_blksize], TFTP_BLOCKSIZE
  1241.  
  1242.         mov cx,[pxe_udp_read_pkt.buffersize]
  1243.         sub cx,2        ; CX <- bytes after opcode
  1244.         jb .failure        ; Garbled reply
  1245.  
  1246.         mov si,packet_buf
  1247.         lodsw
  1248.  
  1249.         cmp ax, TFTP_ERROR
  1250.         je .bailnow        ; ERROR reply: don't try again
  1251.  
  1252.         cmp ax, TFTP_OACK
  1253.         jne .no_tsize
  1254.  
  1255.         ; Now we need to parse the OACK packet to get the transfer
  1256.         ; size.  SI -> first byte of options; CX -> byte count
  1257. .parse_oack:
  1258.         jcxz .no_tsize            ; No options acked
  1259. .get_opt_name:
  1260.         mov di,si
  1261.         mov bx,si
  1262. .opt_name_loop:    lodsb
  1263.         and al,al
  1264.         jz .got_opt_name
  1265.         or al,20h            ; Convert to lowercase
  1266.         stosb
  1267.         loop .opt_name_loop
  1268.         ; We ran out, and no final null
  1269.         jmp .err_reply
  1270. .got_opt_name:    ; si -> option value
  1271.         dec cx                ; bytes left in pkt
  1272.         jz .err_reply            ; Option w/o value
  1273.  
  1274.         ; Parse option pointed to by bx; guaranteed to be
  1275.         ; null-terminated.
  1276.         push cx
  1277.         push si
  1278.         mov si,bx            ; -> option name
  1279.         mov bx,tftp_opt_table
  1280.         mov cx,tftp_opts
  1281. .opt_loop:
  1282.         push cx
  1283.         push si
  1284.         mov di,[bx]            ; Option pointer
  1285.         mov cx,[bx+2]            ; Option len
  1286.         repe cmpsb
  1287.         pop si
  1288.         pop cx
  1289.         je .get_value            ; OK, known option
  1290.         add bx,6
  1291.         loop .opt_loop
  1292.  
  1293.         pop si
  1294.         pop cx
  1295.         jmp .err_reply            ; Non-negotiated option returned
  1296.  
  1297. .get_value:    pop si                ; si -> option value
  1298.         pop cx                ; cx -> bytes left in pkt
  1299.         mov bx,[bx+4]            ; Pointer to data target
  1300.         add bx,[bp-6]            ; TFTP socket pointer
  1301.         xor eax,eax
  1302.         xor edx,edx
  1303. .value_loop:    lodsb
  1304.         and al,al
  1305.         jz .got_value
  1306.         sub al,'0'
  1307.         cmp al, 9
  1308.         ja .err_reply            ; Not a decimal digit
  1309.         imul edx,10
  1310.         add edx,eax
  1311.         mov [bx],edx
  1312.         loop .value_loop
  1313.         ; Ran out before final null, accept anyway
  1314.         jmp short .done_pkt
  1315.  
  1316. .got_value:
  1317.         dec cx
  1318.         jnz .get_opt_name        ; Not end of packet
  1319.  
  1320.         ; ZF == 1
  1321.  
  1322.         ; Success, done!
  1323. .done_pkt:
  1324.         pop si            ; Junk    
  1325.         pop si            ; We want the packet ptr in SI
  1326.  
  1327.         mov eax,[si+tftp_filesize]
  1328.         cmp eax,-1
  1329.         jz .no_tsize
  1330.         mov edx,eax
  1331.         shr edx,16        ; DX:AX == EAX
  1332.  
  1333.         and eax,eax        ; Set ZF depending on file size
  1334.         pop bp            ; Junk
  1335.         pop bp            ; Junk (retry counter)
  1336.         jz .error_si        ; ZF = 1 need to free the socket
  1337.         pop bp
  1338.         pop es
  1339.         ret
  1340.  
  1341. .no_tsize:
  1342. .err_reply:    ; Option negotiation error.  Send ERROR reply.
  1343.         ; ServerIP and gateway are already programmed in
  1344.         mov si,[bp-6]
  1345.         mov ax,[si+tftp_remoteport]
  1346.         mov word [pxe_udp_write_pkt.rport],ax
  1347.         mov word [pxe_udp_write_pkt.buffer],tftp_opt_err
  1348.         mov word [pxe_udp_write_pkt.buffersize],tftp_opt_err_len
  1349.         mov di,pxe_udp_write_pkt
  1350.         mov bx,PXENV_UDP_WRITE
  1351.         call pxenv
  1352.  
  1353.         ; Write an error message and explode
  1354.         mov si,err_oldtftp
  1355.         call writestr
  1356.         jmp kaboom
  1357.  
  1358. .bailnow:    mov word [bp-2],1    ; Immediate error - no retry
  1359.  
  1360. .failure:    pop bx            ; Junk
  1361.         pop bx
  1362.         pop si
  1363.         pop ax
  1364.         dec ax            ; Retry counter
  1365.         jnz .sendreq        ; Try again
  1366.  
  1367. .error:        mov si,bx        ; Socket pointer
  1368. .error_si:                ; Socket pointer already in SI
  1369.         call free_socket    ; ZF <- 1, SI <- 0
  1370.         pop bp
  1371.         pop es
  1372.         ret
  1373.  
  1374. ;
  1375. ; allocate_socket: Allocate a local UDP port structure
  1376. ;
  1377. ;        If successful:
  1378. ;          ZF set
  1379. ;          BX     = socket pointer
  1380. ;               If unsuccessful:
  1381. ;                 ZF clear
  1382. ;
  1383. allocate_socket:
  1384.         push cx
  1385.         mov bx,Files
  1386.         mov cx,MAX_OPEN
  1387. .check:        cmp word [bx], byte 0
  1388.         je .found
  1389.         add bx,open_file_t_size
  1390.         loop .check
  1391.         xor cx,cx            ; ZF = 1
  1392.         pop cx
  1393.         ret
  1394.         ; Allocate a socket number.  Socket numbers are made
  1395.         ; guaranteed unique by including the socket slot number
  1396.         ; (inverted, because we use the loop counter cx); add a
  1397.         ; counter value to keep the numbers from being likely to
  1398.         ; get immediately reused.
  1399.         ;
  1400.         ; The NextSocket variable also contains the top two bits
  1401.         ; set.  This generates a value in the range 49152 to
  1402.         ; 57343.
  1403. .found:
  1404.         dec cx
  1405.         push ax
  1406.         mov ax,[NextSocket]
  1407.         inc ax
  1408.         and ax,((1 << (13-MAX_OPEN_LG2))-1) | 0xC000
  1409.         mov [NextSocket],ax
  1410.         shl cx,13-MAX_OPEN_LG2
  1411.         add cx,ax            ; ZF = 0
  1412.         xchg ch,cl            ; Convert to network byte order
  1413.         mov [bx],cx            ; Socket in use
  1414.         pop ax
  1415.         pop cx
  1416.         ret
  1417.  
  1418. ;
  1419. ; Free socket: socket in SI; return SI = 0, ZF = 1 for convenience
  1420. ;
  1421. free_socket:
  1422.         push es
  1423.         pusha
  1424.         xor ax,ax
  1425.         mov es,ax
  1426.         mov di,si
  1427.         mov cx,tftp_pktbuf >> 1        ; tftp_pktbuf is not cleared
  1428.         rep stosw
  1429.         popa
  1430.         pop es
  1431.         xor si,si
  1432.         ret
  1433.  
  1434. ;
  1435. ; parse_dotquad:
  1436. ;           Read a dot-quad pathname in DS:SI and output an IP
  1437. ;           address in EAX, with SI pointing to the first
  1438. ;           nonmatching character.
  1439. ;
  1440. ;           Return CF=1 on error.
  1441. ;
  1442. parse_dotquad:
  1443.         push cx
  1444.         mov cx,4
  1445.         xor eax,eax
  1446. .parseloop:
  1447.         mov ch,ah
  1448.         mov ah,al
  1449.         lodsb
  1450.         sub al,'0'
  1451.         jb .notnumeric
  1452.         cmp al,9
  1453.         ja .notnumeric
  1454.         aad                ; AL += 10 * AH; AH = 0;
  1455.         xchg ah,ch
  1456.         jmp .parseloop
  1457. .notnumeric:
  1458.         cmp al,'.'-'0'
  1459.         pushf
  1460.         mov al,ah
  1461.         mov ah,ch
  1462.         xor ch,ch
  1463.         ror eax,8
  1464.         popf
  1465.         jne .error
  1466.         loop .parseloop
  1467.         jmp .done
  1468. .error:
  1469.         loop .realerror            ; If CX := 1 then we're done
  1470.         clc
  1471.         jmp .done
  1472. .realerror:
  1473.         stc
  1474. .done:
  1475.         dec si                ; CF unchanged!
  1476.         pop cx
  1477.         ret
  1478. ;
  1479. ; mangle_name: Mangle a filename pointed to by DS:SI into a buffer pointed
  1480. ;           to by ES:DI; ends on encountering any whitespace.
  1481. ;
  1482. ;           This verifies that a filename is < FILENAME_MAX characters
  1483. ;           and doesn't contain whitespace, and zero-pads the output buffer,
  1484. ;           so "repe cmpsb" can do a compare.
  1485. ;
  1486. ;           The first four bytes of the manged name is the IP address of
  1487. ;           the download host.
  1488. ;
  1489. mangle_name:
  1490.         push si
  1491.         mov eax,[ServerIP]
  1492.         cmp byte [si],0
  1493.         je .noip            ; Null filename?!?!
  1494.         cmp word [si],'::'        ; Leading ::?
  1495.         je .gotprefix
  1496.  
  1497. .more:
  1498.         inc si
  1499.         cmp byte [si],0
  1500.         je .noip
  1501.         cmp word [si],'::'
  1502.         jne .more
  1503.  
  1504.         ; We have a :: prefix of some sort, it could be either
  1505.         ; a DNS name or a dot-quad IP address.  Try the dot-quad
  1506.         ; first...
  1507. .here:
  1508.         pop si
  1509.         push si
  1510.         call parse_dotquad
  1511.         jc .notdq
  1512.         cmp word [si],'::'
  1513.         je .gotprefix
  1514. .notdq:
  1515.         pop si
  1516.         push si
  1517.         call dns_resolv
  1518.         cmp word [si],'::'
  1519.         jne .noip
  1520.         and eax,eax
  1521.         jnz .gotprefix
  1522.  
  1523. .noip:
  1524.         pop si
  1525.         xor eax,eax
  1526.         jmp .prefix_done
  1527.  
  1528. .gotprefix:
  1529.         pop cx                ; Adjust stack
  1530.         inc si                ; Skip double colon
  1531.         inc si
  1532.  
  1533. .prefix_done:
  1534.         stosd                ; Save IP address prefix
  1535.         mov cx,FILENAME_MAX-5
  1536.  
  1537. .mn_loop:
  1538.         lodsb
  1539.         cmp al,' '            ; If control or space, end
  1540.         jna .mn_end
  1541.         stosb
  1542.         loop .mn_loop
  1543. .mn_end:
  1544.         inc cx                ; At least one null byte
  1545.         xor ax,ax            ; Zero-fill name
  1546.         rep stosb            ; Doesn't do anything if CX=0
  1547.         ret                ; Done
  1548.  
  1549. ;
  1550. ; unmangle_name: Does the opposite of mangle_name; converts a DOS-mangled
  1551. ;                filename to the conventional representation.  This is needed
  1552. ;                for the BOOT_IMAGE= parameter for the kernel.
  1553. ;                NOTE: A 13-byte buffer is mandatory, even if the string is
  1554. ;                known to be shorter.
  1555. ;
  1556. ;                DS:SI -> input mangled file name
  1557. ;                ES:DI -> output buffer
  1558. ;
  1559. ;                On return, DI points to the first byte after the output name,
  1560. ;                which is set to a null byte.
  1561. ;
  1562. unmangle_name:
  1563.         push eax
  1564.         lodsd
  1565.         and eax,eax
  1566.         jz .noip
  1567.         call gendotquad
  1568.         mov ax,'::'
  1569.         stosw
  1570. .noip:
  1571.         call strcpy
  1572.         dec di                ; Point to final null byte
  1573.         pop eax
  1574.         ret
  1575.  
  1576. ;
  1577. ; pxenv
  1578. ;
  1579. ; This is the main PXENV+/!PXE entry point, using the PXENV+
  1580. ; calling convention.  This is a separate local routine so
  1581. ; we can hook special things from it if necessary.
  1582. ;
  1583.  
  1584. pxenv:
  1585. .jump:        call 0:pxe_thunk        ; Default to calling the thunk
  1586.         cld                ; Make sure DF <- 0
  1587.         ret
  1588.  
  1589. ; Must be after function def due to NASM bug
  1590. PXENVEntry    equ pxenv.jump+1
  1591.  
  1592. ;
  1593. ; pxe_thunk
  1594. ;
  1595. ; Convert from the PXENV+ calling convention (BX, ES, DI) to the !PXE
  1596. ; calling convention (using the stack.)
  1597. ;
  1598. ; This is called as a far routine so that we can just stick it into
  1599. ; the PXENVEntry variable.
  1600. ;
  1601. pxe_thunk:    push es
  1602.         push di
  1603.         push bx
  1604. .jump:        call 0:0
  1605.         add sp,byte 6
  1606.         cmp ax,byte 1
  1607.         cmc                ; Set CF unless ax == 0
  1608.         retf
  1609.  
  1610. ; Must be after function def due to NASM bug
  1611. PXEEntry    equ pxe_thunk.jump+1
  1612.  
  1613. ;
  1614. ; getfssec: Get multiple clusters from a file, given the starting cluster.
  1615. ;
  1616. ;    In this case, get multiple blocks from a specific TCP connection.
  1617. ;
  1618. ;  On entry:
  1619. ;    ES:BX    -> Buffer
  1620. ;    SI    -> TFTP socket pointer
  1621. ;    CX    -> 512-byte block count; 0FFFFh = until end of file
  1622. ;  On exit:
  1623. ;    SI    -> TFTP socket pointer (or 0 on EOF)
  1624. ;    CF = 1    -> Hit EOF
  1625. ;
  1626. getfssec:    push si
  1627.         push fs
  1628.         mov di,bx
  1629.         mov bx,si
  1630.         mov ax,pktbuf_seg
  1631.         mov fs,ax
  1632.  
  1633.         movzx ecx,cx
  1634.         shl ecx,TFTP_BLOCKSIZE_LG2    ; Convert to bytes
  1635.         jz .hit_eof            ; Nothing to do?
  1636.         
  1637. .need_more:
  1638.         push ecx
  1639.  
  1640.         movzx eax,word [bx+tftp_bytesleft]
  1641.         cmp ecx,eax
  1642.         jna .ok_size
  1643.         mov ecx,eax
  1644.         jcxz .need_packet        ; No bytes available?
  1645. .ok_size:
  1646.  
  1647.         mov ax,cx            ; EAX<31:16> == ECX<31:16> == 0
  1648.         mov si,[bx+tftp_dataptr]
  1649.         sub [bx+tftp_bytesleft],cx
  1650.         fs rep movsb            ; Copy from packet buffer
  1651.         mov [bx+tftp_dataptr],si
  1652.  
  1653.         pop ecx
  1654.         sub ecx,eax
  1655.         jnz .need_more
  1656.  
  1657.  
  1658. .hit_eof:
  1659.         pop fs
  1660.         pop si
  1661.  
  1662.         ; Is there anything left of this?
  1663.         mov eax,[si+tftp_filesize]
  1664.         sub eax,[si+tftp_filepos]
  1665.         jnz .bytes_left    ; CF <- 0
  1666.  
  1667.         cmp [si+tftp_bytesleft],ax
  1668.         jnz .bytes_left    ; CF <- 0
  1669.  
  1670.         ; The socket is closed and the buffer drained
  1671.         ; Close socket structure and re-init for next user
  1672.         call free_socket
  1673.         stc
  1674. .bytes_left:
  1675.         ret
  1676.  
  1677. ;
  1678. ; No data in buffer, check to see if we can get a packet...
  1679. ;
  1680. .need_packet:
  1681.         pop ecx
  1682.         mov eax,[bx+tftp_filesize]
  1683.         cmp eax,[bx+tftp_filepos]
  1684.         je .hit_eof            ; Already EOF'd; socket already closed
  1685.  
  1686.         pushad
  1687.         push es
  1688.         mov si,bx
  1689.         call get_packet
  1690.         pop es
  1691.         popad
  1692.  
  1693.         jmp .need_more
  1694.  
  1695. ;
  1696. ; Get a fresh packet; expects fs -> pktbuf_seg and ds:si -> socket structure
  1697. ;
  1698. get_packet:
  1699.         mov ax,ds
  1700.         mov es,ax
  1701.     
  1702. .packet_loop:
  1703.         ; Start by ACKing the previous packet; this should cause the
  1704.         ; next packet to be sent.
  1705.         mov cx,PKT_RETRY
  1706.         mov word [PktTimeout],PKT_TIMEOUT
  1707.  
  1708. .send_ack:    push cx                ; <D> Retry count
  1709.  
  1710.         mov ax,[si+tftp_lastpkt]
  1711.         call ack_packet            ; Send ACK
  1712.  
  1713.         ; We used to test the error code here, but sometimes
  1714.         ; PXE would return negative status even though we really
  1715.         ; did send the ACK.  Now, just treat a failed send as
  1716.         ; a normally lost packet, and let it time out in due
  1717.         ; course of events.
  1718.  
  1719. .send_ok:    ; Now wait for packet.
  1720.         mov dx,[BIOS_timer]        ; Get current time
  1721.  
  1722.         mov cx,[PktTimeout]
  1723. .wait_data:    push cx                ; <E> Timeout
  1724.         push dx                ; <F> Old time
  1725.  
  1726.         mov bx,[si+tftp_pktbuf]
  1727.         mov [pxe_udp_read_pkt.buffer],bx
  1728.         mov [pxe_udp_read_pkt.buffer+2],fs
  1729.         mov [pxe_udp_read_pkt.buffersize],word PKTBUF_SIZE
  1730.         mov eax,[si+tftp_remoteip]
  1731.         mov [pxe_udp_read_pkt.sip],eax
  1732.         mov eax,[MyIP]
  1733.         mov [pxe_udp_read_pkt.dip],eax
  1734.         mov ax,[si+tftp_remoteport]
  1735.         mov [pxe_udp_read_pkt.rport],ax
  1736.         mov ax,[si+tftp_localport]
  1737.         mov [pxe_udp_read_pkt.lport],ax
  1738.         mov di,pxe_udp_read_pkt
  1739.         mov bx,PXENV_UDP_READ
  1740.         push si                ; <G>
  1741.         call pxenv
  1742.         pop si                ; <G>
  1743.         and ax,ax
  1744.         jz .recv_ok
  1745.  
  1746.         ; No packet, or receive failure
  1747.         mov dx,[BIOS_timer]
  1748.         pop ax                ; <F> Old time
  1749.         pop cx                ; <E> Timeout
  1750.         cmp ax,dx            ; Same time -> don't advance timeout
  1751.         je .wait_data            ; Same clock tick
  1752.         loop .wait_data            ; Decrease timeout
  1753.         
  1754.         pop cx                ; <D> Didn't get any, send another ACK
  1755.         shl word [PktTimeout],1        ; Exponential backoff
  1756.         loop .send_ack
  1757.         jmp kaboom            ; Forget it...
  1758.  
  1759. .recv_ok:    pop dx                ; <F>
  1760.         pop cx                ; <E>
  1761.  
  1762.         cmp word [pxe_udp_read_pkt.buffersize],byte 4
  1763.         jb .wait_data            ; Bad size for a DATA packet
  1764.  
  1765.         mov bx,[si+tftp_pktbuf]
  1766.         cmp word [fs:bx],TFTP_DATA    ; Not a data packet?
  1767.         jne .wait_data            ; Then wait for something else
  1768.  
  1769.         mov ax,[si+tftp_lastpkt]
  1770.         xchg ah,al            ; Host byte order
  1771.         inc ax                ; Which packet are we waiting for?
  1772.         xchg ah,al            ; Network byte order
  1773.         cmp [fs:bx+2],ax
  1774.         je .right_packet
  1775.  
  1776.         ; Wrong packet, ACK the packet and then try again
  1777.         ; This is presumably because the ACK got lost,
  1778.         ; so the server just resent the previous packet
  1779.         mov ax,[fs:bx+2]
  1780.         call ack_packet
  1781.         jmp .send_ok            ; Reset timeout
  1782.  
  1783. .right_packet:    ; It's the packet we want.  We're also EOF if the size < blocksize
  1784.  
  1785.         pop cx                ; <D> Don't need the retry count anymore
  1786.  
  1787.         mov [si+tftp_lastpkt],ax    ; Update last packet number
  1788.  
  1789.         movzx ecx,word [pxe_udp_read_pkt.buffersize]
  1790.         sub cx,byte 4            ; Skip TFTP header
  1791.  
  1792.         ; If this is a zero-length block, don't mess with the pointers,
  1793.         ; since we may have just set up the previous block that way
  1794.         jz .last_block
  1795.  
  1796.         ; Set pointer to data block
  1797.         lea ax,[bx+4]            ; Data past TFTP header
  1798.         mov [si+tftp_dataptr],ax
  1799.  
  1800.         add [si+tftp_filepos],ecx
  1801.         mov [si+tftp_bytesleft],cx
  1802.  
  1803.         cmp cx,[si+tftp_blksize]    ; Is it a full block?
  1804.         jb .last_block            ; If so, it's not EOF
  1805.  
  1806.         ; If we had the exact right number of bytes, always get
  1807.         ; one more packet to get the (zero-byte) EOF packet and
  1808.         ; close the socket.
  1809.         mov eax,[si+tftp_filepos]
  1810.         cmp [si+tftp_filesize],eax
  1811.         je .packet_loop
  1812.  
  1813.         ret
  1814.  
  1815.  
  1816. .last_block:    ; Last block - ACK packet immediately
  1817.         mov ax,[fs:bx+2]
  1818.         call ack_packet
  1819.  
  1820.         ; Make sure we know we are at end of file
  1821.         mov eax,[si+tftp_filepos]
  1822.         mov [si+tftp_filesize],eax
  1823.     
  1824.         ret
  1825.  
  1826. ;
  1827. ; ack_packet:
  1828. ;
  1829. ; Send ACK packet.  This is a common operation and so is worth canning.
  1830. ;
  1831. ; Entry:
  1832. ;    SI     = TFTP block
  1833. ;    AX     = Packet # to ack (network byte order)
  1834. ; Exit:
  1835. ;    ZF = 0 -> Error
  1836. ;    All registers preserved
  1837. ;
  1838. ; This function uses the pxe_udp_write_pkt but not the packet_buf.
  1839. ;
  1840. ack_packet:
  1841.         pushad
  1842.         mov [ack_packet_buf+2],ax    ; Packet number to ack
  1843.         mov ax,[si]
  1844.         mov [pxe_udp_write_pkt.lport],ax
  1845.         mov ax,[si+tftp_remoteport]
  1846.         mov [pxe_udp_write_pkt.rport],ax
  1847.         mov eax,[si+tftp_remoteip]
  1848.         mov [pxe_udp_write_pkt.sip],eax
  1849.         xor eax,[MyIP]
  1850.         and eax,[Netmask]
  1851.         jz .nogw
  1852.         mov eax,[Gateway]
  1853. .nogw:
  1854.         mov [pxe_udp_write_pkt.gip],eax
  1855.         mov [pxe_udp_write_pkt.buffer],word ack_packet_buf
  1856.         mov [pxe_udp_write_pkt.buffersize], word 4
  1857.         mov di,pxe_udp_write_pkt
  1858.         mov bx,PXENV_UDP_WRITE
  1859.         call pxenv
  1860.         cmp ax,byte 0            ; ZF = 1 if write OK
  1861.         popad
  1862.         ret
  1863.  
  1864. ;
  1865. ; unload_pxe:
  1866. ;
  1867. ; This function unloads the PXE and UNDI stacks and unclaims
  1868. ; the memory.
  1869. ;
  1870. unload_pxe:
  1871.         test byte [KeepPXE],01h        ; Should we keep PXE around?
  1872.         jnz reset_pxe
  1873.  
  1874.         push ds
  1875.         push es
  1876.  
  1877.         mov ax,cs
  1878.         mov ds,ax
  1879.         mov es,ax
  1880.  
  1881.         mov si,new_api_unload
  1882.         cmp byte [APIVer+1],2        ; Major API version >= 2?
  1883.         jae .new_api
  1884.         mov si,old_api_unload
  1885. .new_api:
  1886.         
  1887. .call_loop:    xor ax,ax
  1888.         lodsb
  1889.         and ax,ax
  1890.         jz .call_done
  1891.         xchg bx,ax
  1892.         mov di,pxe_unload_stack_pkt
  1893.         push di
  1894.         xor ax,ax
  1895.         mov cx,pxe_unload_stack_pkt_len >> 1
  1896.         rep stosw
  1897.         pop di
  1898.         call pxenv
  1899.         jc .cant_free
  1900.         mov ax,word [pxe_unload_stack_pkt.status]
  1901.         cmp ax,PXENV_STATUS_SUCCESS
  1902.         jne .cant_free
  1903.         jmp .call_loop
  1904.  
  1905. .call_done:
  1906. ;
  1907. ; This isn't necessary anymore; we can use the memory area previously
  1908. ; used by the PXE stack indefinitely, and the chainload code sets up
  1909. ; a new stack independently.  Leave the source code in here for now,
  1910. ; but expect to rip it out soonish.
  1911. ;
  1912. %if 0 ; USE_PXE_PROVIDED_STACK
  1913.         ; We need to switch to our local stack here...
  1914.         pusha
  1915.         pushf
  1916.         push gs
  1917.  
  1918.         mov si,sp
  1919.         mov ax,ss
  1920.         mov gs,ax
  1921.         sub ax,[BaseStack+4]        ; Are we using the base stack
  1922.         je .is_base_stack        ; (as opposed to the COMBOOT stack)?
  1923.  
  1924.         lgs si,[SavedSSSP]        ; COMBOOT stack
  1925. .is_base_stack:
  1926.  
  1927.         mov cx,[InitStack]
  1928.         mov di,StackBuf
  1929.         mov [BaseStack],di
  1930.         mov [BaseStack+4],es
  1931.         sub cx,si
  1932.         sub di,cx
  1933.         mov [SavedSSSP],di        ; New SP
  1934.         mov [SavedSSSP+2],es
  1935.         gs rep movsb
  1936.  
  1937.         and ax,ax            ; Remember which stack
  1938.         jne .combootstack
  1939.  
  1940.         ; Update the base stack pointer since it's in use
  1941.         lss sp,[SavedSSSP]
  1942.         
  1943. .combootstack:
  1944.         pop gs
  1945.         popf
  1946.         popa
  1947. %endif
  1948.         mov bx,0FF00h
  1949.  
  1950.         mov dx,[RealBaseMem]
  1951.         cmp dx,[BIOS_fbm]        ; Sanity check
  1952.         jna .cant_free
  1953.         inc bx
  1954.  
  1955.         ; Check that PXE actually unhooked the INT 1Ah chain
  1956.         movzx eax,word [4*0x1a]
  1957.         movzx ecx,word [4*0x1a+2]
  1958.         shl ecx,4
  1959.         add eax,ecx
  1960.         shr eax,10
  1961.         cmp ax,dx            ; Not in range
  1962.         jae .ok
  1963.         cmp ax,[BIOS_fbm]
  1964.         jae .cant_free
  1965.         ; inc bx
  1966.  
  1967. .ok:
  1968.         mov [BIOS_fbm],dx
  1969. .pop_ret:
  1970.         pop es
  1971.         pop ds
  1972.         ret
  1973.         
  1974. .cant_free:
  1975.         mov si,cant_free_msg
  1976.         call writestr
  1977.         push ax
  1978.         xchg bx,ax
  1979.         call writehex4
  1980.         mov al,'-'
  1981.         call writechr
  1982.         pop ax
  1983.         call writehex4
  1984.         mov al,'-'
  1985.         call writechr
  1986.         mov eax,[4*0x1a]
  1987.         call writehex8
  1988.         call crlf
  1989.         jmp .pop_ret
  1990.  
  1991.         ; We want to keep PXE around, but still we should reset
  1992.         ; it to the standard bootup configuration
  1993. reset_pxe:
  1994.         push es
  1995.         push cs
  1996.         pop es
  1997.         mov bx,PXENV_UDP_CLOSE
  1998.         mov di,pxe_udp_close_pkt
  1999.         call pxenv
  2000.         pop es
  2001.         ret
  2002.  
  2003. ;
  2004. ; gendotquad
  2005. ;
  2006. ; Take an IP address (in network byte order) in EAX and
  2007. ; output a dotted quad string to ES:DI.
  2008. ; DI points to terminal null at end of string on exit.
  2009. ;
  2010. gendotquad:
  2011.         push eax
  2012.         push cx
  2013.         mov cx,4
  2014. .genchar:
  2015.         push eax
  2016.         cmp al,10        ; < 10?
  2017.         jb .lt10        ; If so, skip first 2 digits
  2018.  
  2019.         cmp al,100        ; < 100
  2020.         jb .lt100        ; If so, skip first digit
  2021.  
  2022.         aam 100
  2023.         ; Now AH = 100-digit; AL = remainder
  2024.         add ah,'0'
  2025.         mov [es:di],ah
  2026.         inc di
  2027.  
  2028. .lt100:
  2029.         aam 10
  2030.         ; Now AH = 10-digit; AL = remainder
  2031.         add ah,'0'
  2032.         mov [es:di],ah        
  2033.         inc di
  2034.  
  2035. .lt10:
  2036.         add al,'0'
  2037.         stosb
  2038.         mov al,'.'
  2039.         stosb
  2040.         pop eax
  2041.         ror eax,8    ; Move next char into LSB
  2042.         loop .genchar
  2043.         dec di
  2044.         mov [es:di], byte 0
  2045.         pop cx
  2046.         pop eax
  2047.         ret
  2048.  
  2049. ;
  2050. ; parse_dhcp
  2051. ;
  2052. ; Parse a DHCP packet.  This includes dealing with "overloaded"
  2053. ; option fields (see RFC 2132, section 9.3)
  2054. ;
  2055. ; This should fill in the following global variables, if the
  2056. ; information is present:
  2057. ;
  2058. ; MyIP        - client IP address
  2059. ; ServerIP    - boot server IP address
  2060. ; Netmask    - network mask
  2061. ; Gateway    - default gateway router IP
  2062. ; BootFile    - boot file name
  2063. ; DNSServers    - DNS server IPs
  2064. ; LocalDomain    - Local domain name
  2065. ;
  2066. ; This assumes the DHCP packet is in "trackbuf" and the length
  2067. ; of the packet in in CX on entry.
  2068. ;
  2069.  
  2070. parse_dhcp:
  2071.         mov byte [OverLoad],0        ; Assume no overload
  2072.         mov eax, [trackbuf+bootp.yip]
  2073.         and eax, eax
  2074.         jz .noyip
  2075.         cmp al,224            ; Class D or higher -> bad
  2076.         jae .noyip
  2077.         mov [MyIP], eax
  2078. .noyip:
  2079.         mov eax, [trackbuf+bootp.sip]
  2080.         and eax, eax
  2081.         jz .nosip
  2082.         cmp al,224            ; Class D or higher -> bad
  2083.         jae .nosip
  2084.         mov [ServerIP], eax
  2085. .nosip:
  2086.         sub cx, bootp.options
  2087.         jbe .nooptions
  2088.         mov si, trackbuf+bootp.option_magic
  2089.         lodsd
  2090.         cmp eax, BOOTP_OPTION_MAGIC
  2091.         jne .nooptions
  2092.         call parse_dhcp_options
  2093. .nooptions:
  2094.         mov si, trackbuf+bootp.bootfile
  2095.         test byte [OverLoad],1
  2096.         jz .nofileoverload
  2097.         mov cx,128
  2098.         call parse_dhcp_options
  2099.         jmp short .parsed_file
  2100. .nofileoverload:
  2101.         cmp byte [si], 0
  2102.         jz .parsed_file            ; No bootfile name
  2103.         mov di,BootFile
  2104.         mov cx,32
  2105.         rep movsd
  2106.         xor al,al
  2107.         stosb                ; Null-terminate
  2108. .parsed_file:
  2109.         mov si, trackbuf+bootp.sname
  2110.         test byte [OverLoad],2
  2111.         jz .nosnameoverload
  2112.         mov cx,64
  2113.         call parse_dhcp_options
  2114. .nosnameoverload:
  2115.         ret
  2116.  
  2117. ;
  2118. ; Parse a sequence of DHCP options, pointed to by DS:SI; the field
  2119. ; size is CX -- some DHCP servers leave option fields unterminated
  2120. ; in violation of the spec.
  2121. ;
  2122. ; For parse_some_dhcp_options, DH contains the minimum value for
  2123. ; the option to recognize -- this is used to restrict parsing to
  2124. ; PXELINUX-specific options only.
  2125. ;
  2126. parse_dhcp_options:
  2127.         xor dx,dx
  2128.  
  2129. parse_some_dhcp_options:
  2130. .loop:
  2131.         and cx,cx
  2132.         jz .done
  2133.  
  2134.         lodsb
  2135.         dec cx
  2136.         jz .done    ; Last byte; must be PAD, END or malformed
  2137.         cmp al, 0    ; PAD option
  2138.         je .loop
  2139.         cmp al,255    ; END option
  2140.         je .done
  2141.  
  2142.         ; Anything else will have a length field
  2143.         mov dl,al    ; DL <- option number
  2144.         xor ax,ax
  2145.         lodsb        ; AX <- option length
  2146.         dec cx
  2147.         sub cx,ax    ; Decrement bytes left counter
  2148.         jb .done    ; Malformed option: length > field size
  2149.  
  2150.         cmp dl,dh    ; Is the option value valid?
  2151.         jb .opt_done
  2152.  
  2153.         cmp dl,1    ; SUBNET MASK option
  2154.         jne .not_subnet
  2155.         mov ebx,[si]
  2156.         mov [Netmask],ebx
  2157.         jmp .opt_done
  2158. .not_subnet:
  2159.  
  2160.         cmp dl,3    ; ROUTER option
  2161.         jne .not_router
  2162.         mov ebx,[si]
  2163.         mov [Gateway],ebx
  2164.         jmp .opt_done
  2165. .not_router:
  2166.  
  2167.         cmp dl,6    ; DNS SERVERS option
  2168.         jne .not_dns
  2169.         pusha
  2170.         mov cx,ax
  2171.         shr cx,2
  2172.         cmp cl,DNS_MAX_SERVERS
  2173.         jna .oklen
  2174.         mov cl,DNS_MAX_SERVERS
  2175. .oklen:
  2176.         mov di,DNSServers
  2177.         rep movsd
  2178.         mov [LastDNSServer],di
  2179.         popa
  2180.         jmp .opt_done
  2181. .not_dns:
  2182.  
  2183.         cmp dl,15    ; DNS LOCAL DOMAIN option
  2184.         jne .not_localdomain
  2185.         pusha
  2186.         mov bx,si
  2187.         add bx,ax
  2188.         xor ax,ax
  2189.         xchg [bx],al    ; Zero-terminate option
  2190.         mov di,LocalDomain
  2191.         call dns_mangle    ; Convert to DNS label set
  2192.         mov [bx],al    ; Restore ending byte
  2193.         popa
  2194.         jmp .opt_done
  2195. .not_localdomain:
  2196.  
  2197.         cmp dl,43    ; VENDOR ENCAPSULATED option
  2198.         jne .not_vendor
  2199.         pusha
  2200.         mov dh,208    ; Only recognize PXELINUX options
  2201.         mov cx,ax    ; Length of option = max bytes to parse
  2202.         call parse_some_dhcp_options    ; Parse recursive structure
  2203.         popa
  2204.         jmp .opt_done
  2205. .not_vendor:
  2206.  
  2207.         cmp dl,52    ; OPTION OVERLOAD option
  2208.         jne .not_overload
  2209.         mov bl,[si]
  2210.         mov [OverLoad],bl
  2211.         jmp .opt_done
  2212. .not_overload:
  2213.  
  2214.         cmp dl,67    ; BOOTFILE NAME option
  2215.         jne .not_bootfile
  2216.         mov di,BootFile
  2217.         jmp short .copyoption
  2218. .done:
  2219.         ret        ; This is here to make short jumps easier
  2220. .not_bootfile:
  2221.  
  2222.         cmp dl,208    ; PXELINUX MAGIC option
  2223.         jne .not_pl_magic
  2224.         cmp al,4    ; Must have length == 4
  2225.         jne .opt_done
  2226.         cmp dword [si], htonl(0xF100747E)    ; Magic number
  2227.         jne .opt_done
  2228.         or byte [DHCPMagic], byte 1        ; Found magic #
  2229.         jmp short .opt_done
  2230. .not_pl_magic:
  2231.  
  2232.         cmp dl,209    ; PXELINUX CONFIGFILE option
  2233.         jne .not_pl_config
  2234.         mov di,ConfigName
  2235.         or byte [DHCPMagic], byte 2    ; Got config file
  2236.         jmp short .copyoption
  2237. .not_pl_config:
  2238.  
  2239.         cmp dl,210    ; PXELINUX PATHPREFIX option
  2240.         jne .not_pl_prefix
  2241.         mov di,PathPrefix
  2242.         or byte [DHCPMagic], byte 4    ; Got path prefix
  2243.         jmp short .copyoption
  2244. .not_pl_prefix:
  2245.  
  2246.         cmp dl,211    ; PXELINUX REBOOTTIME option
  2247.         jne .not_pl_timeout
  2248.         cmp al,4
  2249.         jne .opt_done
  2250.         mov ebx,[si]
  2251.         xchg bl,bh    ; Convert to host byte order
  2252.         rol ebx,16
  2253.         xchg bl,bh
  2254.         mov [RebootTime],ebx
  2255.         or byte [DHCPMagic], byte 8    ; Got RebootTime
  2256.         ; jmp short .opt_done
  2257. .not_pl_timeout:
  2258.  
  2259.         ; Unknown option.  Skip to the next one.
  2260. .opt_done:
  2261.         add si,ax
  2262. .opt_done_noskip:
  2263.         jmp .loop
  2264.  
  2265.         ; Common code for copying an option verbatim
  2266. .copyoption:
  2267.         xchg cx,ax
  2268.         rep movsb
  2269.         xchg cx,ax    ; Now ax == 0
  2270.         stosb        ; Null-terminate
  2271.         jmp short .opt_done_noskip
  2272.  
  2273. ;
  2274. ; genipopt
  2275. ;
  2276. ; Generate an ip=<client-ip>:<boot-server-ip>:<gw-ip>:<netmask>
  2277. ; option into IPOption based on a DHCP packet in trackbuf.
  2278. ; Assumes CS == DS == ES.
  2279. ;
  2280. genipopt:
  2281.         pushad
  2282.         mov di,IPOption
  2283.         mov eax,'ip='
  2284.         stosd
  2285.         dec di
  2286.         mov eax,[MyIP]
  2287.         call gendotquad
  2288.         mov al,':'
  2289.         stosb
  2290.         mov eax,[ServerIP]
  2291.         call gendotquad
  2292.         mov al,':'
  2293.         stosb
  2294.         mov eax,[Gateway]
  2295.         call gendotquad
  2296.         mov al,':'
  2297.         stosb
  2298.         mov eax,[Netmask]
  2299.         call gendotquad    ; Zero-terminates its output
  2300.         sub di,IPOption
  2301.         mov [IPOptionLen],di
  2302.         popad
  2303.         ret
  2304.  
  2305. ;
  2306. ; Call the receive loop while idle.  This is done mostly so we can respond to
  2307. ; ARP messages, but perhaps in the future this can be used to do network
  2308. ; console.
  2309. ;
  2310. ; hpa sez: people using automatic control on the serial port get very
  2311. ; unhappy if we poll for ARP too often (the PXE stack is pretty slow,
  2312. ; typically.)  Therefore, only poll if at least 4 BIOS timer ticks have
  2313. ; passed since the last poll, and reset this when a character is
  2314. ; received (RESET_IDLE).
  2315. ;
  2316. reset_idle:
  2317.         push ax
  2318.         mov ax,[cs:BIOS_timer]
  2319.         mov [cs:IdleTimer],ax
  2320.         pop ax
  2321.         ret
  2322.  
  2323. check_for_arp:
  2324.         push ax
  2325.         mov ax,[cs:BIOS_timer]
  2326.         sub ax,[cs:IdleTimer]
  2327.         cmp ax,4
  2328.         pop ax
  2329.         jae .need_poll
  2330.         ret
  2331. .need_poll:    pushad
  2332.         push ds
  2333.         push es
  2334.         mov ax,cs
  2335.         mov ds,ax
  2336.         mov es,ax
  2337.         mov di,packet_buf
  2338.         mov [pxe_udp_read_pkt.status],al    ; 0
  2339.         mov [pxe_udp_read_pkt.buffer],di
  2340.         mov [pxe_udp_read_pkt.buffer+2],ds
  2341.         mov word [pxe_udp_read_pkt.buffersize],packet_buf_size
  2342.         mov eax,[MyIP]
  2343.         mov [pxe_udp_read_pkt.dip],eax
  2344.         mov [pxe_udp_read_pkt.lport],ax        ; 0
  2345.         mov di,pxe_udp_read_pkt
  2346.         mov bx,PXENV_UDP_READ
  2347.         call pxenv
  2348.         ; Ignore result...
  2349.         pop es
  2350.         pop ds
  2351.         popad
  2352.         RESET_IDLE
  2353.         ret
  2354.  
  2355. ; -----------------------------------------------------------------------------
  2356. ;  Common modules
  2357. ; -----------------------------------------------------------------------------
  2358.  
  2359. %include "getc.inc"        ; getc et al
  2360. %include "conio.inc"        ; Console I/O
  2361. %include "writestr.inc"        ; String output
  2362. writestr    equ cwritestr
  2363. %include "writehex.inc"        ; Hexadecimal output
  2364. %include "parseconfig.inc"    ; High-level config file handling
  2365. %include "parsecmd.inc"        ; Low-level config file handling
  2366. %include "bcopy32.inc"        ; 32-bit bcopy
  2367. %include "loadhigh.inc"        ; Load a file into high memory
  2368. %include "font.inc"        ; VGA font stuff
  2369. %include "graphics.inc"        ; VGA graphics
  2370. %include "highmem.inc"        ; High memory sizing
  2371. %include "strcpy.inc"        ; strcpy()
  2372. %include "rawcon.inc"        ; Console I/O w/o using the console functions
  2373. %include "dnsresolv.inc"    ; DNS resolver
  2374.  
  2375. ; -----------------------------------------------------------------------------
  2376. ;  Begin data section
  2377. ; -----------------------------------------------------------------------------
  2378.  
  2379.         section .data
  2380.  
  2381. hextbl_lower    db '0123456789abcdef'
  2382. copyright_str   db ' Copyright (C) 1994-', year, ' H. Peter Anvin'
  2383.         db CR, LF, 0
  2384. boot_prompt    db 'boot: ', 0
  2385. wipe_char    db BS, ' ', BS, 0
  2386. err_notfound    db 'Could not find kernel image: ',0
  2387. err_notkernel    db CR, LF, 'Invalid or corrupt kernel image.', CR, LF, 0
  2388. err_noram    db 'It appears your computer has less than '
  2389.         asciidec dosram_k
  2390.         db 'K of low ("DOS")'
  2391.         db CR, LF
  2392.         db 'RAM.  Linux needs at least this amount to boot.  If you get'
  2393.         db CR, LF
  2394.         db 'this message in error, hold down the Ctrl key while'
  2395.         db CR, LF
  2396.         db 'booting, and I will take your word for it.', CR, LF, 0
  2397. err_badcfg      db 'Unknown keyword in config file.', CR, LF, 0
  2398. err_noparm      db 'Missing parameter in config file.', CR, LF, 0
  2399. err_noinitrd    db CR, LF, 'Could not find ramdisk image: ', 0
  2400. err_nohighmem   db 'Not enough memory to load specified kernel.', CR, LF, 0
  2401. err_highload    db CR, LF, 'Kernel transfer failure.', CR, LF, 0
  2402. err_oldkernel   db 'Cannot load a ramdisk with an old kernel image.'
  2403.                 db CR, LF, 0
  2404. err_notdos    db ': attempted DOS system call', CR, LF, 0
  2405. err_comlarge    db 'COMBOOT image too large.', CR, LF, 0
  2406. err_bssimage    db 'BSS images not supported.', CR, LF, 0
  2407. err_a20        db CR, LF, 'A20 gate not responding!', CR, LF, 0
  2408. err_bootfailed    db CR, LF, 'Boot failed: press a key to retry, or wait for reset...', CR, LF, 0
  2409. bailmsg        equ err_bootfailed
  2410. err_nopxe    db "No !PXE or PXENV+ API found; we're dead...", CR, LF, 0
  2411. err_pxefailed    db 'PXE API call failed, error ', 0
  2412. err_udpinit    db 'Failed to initialize UDP stack', CR, LF, 0
  2413. err_oldtftp    db 'TFTP server does not support the tsize option', CR, LF, 0
  2414. found_pxenv    db 'Found PXENV+ structure', CR, LF, 0
  2415. using_pxenv_msg db 'Old PXE API detected, using PXENV+ structure', CR, LF, 0
  2416. apiver_str    db 'PXE API version is ',0
  2417. pxeentry_msg    db 'PXE entry point found (we hope) at ', 0
  2418. pxenventry_msg    db 'PXENV entry point found (we hope) at ', 0
  2419. trymempxe_msg    db 'Scanning memory for !PXE structure... ', 0
  2420. trymempxenv_msg    db 'Scanning memory for PXENV+ structure... ', 0
  2421. undi_data_msg      db 'UNDI data segment at:   ',0
  2422. undi_data_len_msg db 'UNDI data segment size: ',0 
  2423. undi_code_msg      db 'UNDI code segment at:   ',0
  2424. undi_code_len_msg db 'UNDI code segment size: ',0 
  2425. cant_free_msg    db 'Failed to free base memory, error ', 0
  2426. notfound_msg    db 'not found', CR, LF, 0
  2427. myipaddr_msg    db 'My IP address seems to be ',0
  2428. tftpprefix_msg    db 'TFTP prefix: ', 0
  2429. localboot_msg    db 'Booting from local disk...', CR, LF, 0
  2430. cmdline_msg    db 'Command line: ', CR, LF, 0
  2431. ready_msg    db 'Ready.', CR, LF, 0
  2432. trying_msg    db 'Trying to load: ', 0
  2433. crlfloading_msg    db CR, LF            ; Fall through
  2434. loading_msg     db 'Loading ', 0
  2435. dotdot_msg      db '.'
  2436. dot_msg         db '.', 0
  2437. fourbs_msg    db BS, BS, BS, BS, 0
  2438. aborted_msg    db ' aborted.'            ; Fall through to crlf_msg!
  2439. crlf_msg    db CR, LF
  2440. null_msg    db 0
  2441. crff_msg    db CR, FF, 0
  2442. default_str    db 'default', 0
  2443. default_len    equ ($-default_str)
  2444. syslinux_banner    db CR, LF, 'PXELINUX ', version_str, ' ', date, ' ', 0
  2445. cfgprefix    db 'pxelinux.cfg/'        ; No final null!
  2446. cfgprefix_len    equ ($-cfgprefix)
  2447.  
  2448. ;
  2449. ; Command line options we'd like to take a look at
  2450. ;
  2451. ; mem= and vga= are handled as normal 32-bit integer values
  2452. initrd_cmd    db 'initrd='
  2453. initrd_cmd_len    equ $-initrd_cmd
  2454.  
  2455. ; This one we make ourselves
  2456. bootif_str    db 'BOOTIF='
  2457. bootif_str_len    equ $-bootif_str
  2458. ;
  2459. ; Config file keyword table
  2460. ;
  2461. %include "keywords.inc"
  2462.  
  2463. ;
  2464. ; Extensions to search for (in *forward* order).
  2465. ; (.bs and .bss are disabled for PXELINUX, since they are not supported)
  2466. ;
  2467.         align 4, db 0
  2468. exten_table:    db '.cbt'        ; COMBOOT (specific)
  2469.         db '.0', 0, 0        ; PXE bootstrap program
  2470.         db '.com'        ; COMBOOT (same as DOS)
  2471.         db '.c32'        ; COM32
  2472. exten_table_end:
  2473.         dd 0, 0            ; Need 8 null bytes here
  2474.  
  2475. ;
  2476. ; PXE unload sequences
  2477. ;
  2478. new_api_unload:
  2479.         db PXENV_UDP_CLOSE
  2480.         db PXENV_UNDI_SHUTDOWN
  2481.         db PXENV_UNLOAD_STACK
  2482.         db PXENV_STOP_UNDI
  2483.         db 0
  2484. old_api_unload:
  2485.         db PXENV_UDP_CLOSE
  2486.         db PXENV_UNDI_SHUTDOWN
  2487.         db PXENV_UNLOAD_STACK
  2488.         db PXENV_UNDI_CLEANUP
  2489.         db 0
  2490.  
  2491. ;
  2492. ; PXE query packets partially filled in
  2493. ;
  2494. pxe_bootp_query_pkt_2:
  2495. .status:    dw 0            ; Status
  2496. .packettype:    dw 2            ; DHCPACK packet
  2497. .buffersize:    dw trackbufsize        ; Packet size
  2498. .buffer:    dw trackbuf, 0        ; seg:off of buffer
  2499. .bufferlimit:    dw trackbufsize        ; Unused
  2500.  
  2501. pxe_bootp_query_pkt_3:
  2502. .status:    dw 0            ; Status
  2503. .packettype:    dw 3            ; Boot server packet
  2504. .buffersize:    dw trackbufsize        ; Packet size
  2505. .buffer:    dw trackbuf, 0        ; seg:off of buffer
  2506. .bufferlimit:    dw trackbufsize        ; Unused
  2507.  
  2508. pxe_bootp_size_query_pkt:
  2509. .status:    dw 0            ; Status
  2510. .packettype:    dw 2            ; DHCPACK packet
  2511. .buffersize:    dw 0            ; Packet size
  2512. .buffer:    dw 0, 0            ; seg:off of buffer
  2513. .bufferlimit:    dw 0            ; Unused
  2514.  
  2515. pxe_udp_open_pkt:
  2516. .status:    dw 0            ; Status
  2517. .sip:        dd 0            ; Source (our) IP
  2518.  
  2519. pxe_udp_close_pkt:
  2520. .status:    dw 0            ; Status
  2521.  
  2522. pxe_udp_write_pkt:
  2523. .status:    dw 0            ; Status
  2524. .sip:        dd 0            ; Server IP
  2525. .gip:        dd 0            ; Gateway IP
  2526. .lport:        dw 0            ; Local port
  2527. .rport:        dw 0            ; Remote port
  2528. .buffersize:    dw 0            ; Size of packet
  2529. .buffer:    dw 0, 0            ; seg:off of buffer
  2530.  
  2531. pxe_udp_read_pkt:
  2532. .status:    dw 0            ; Status
  2533. .sip:        dd 0            ; Source IP
  2534. .dip:        dd 0            ; Destination (our) IP
  2535. .rport:        dw 0            ; Remote port
  2536. .lport:        dw 0            ; Local port
  2537. .buffersize:    dw 0            ; Max packet size
  2538. .buffer:    dw 0, 0            ; seg:off of buffer
  2539.  
  2540. ;
  2541. ; Misc initialized (data) variables
  2542. ;
  2543.         alignb 4, db 0
  2544. BaseStack    dd StackBuf        ; SS:ESP of base stack
  2545. NextSocket    dw 49152        ; Counter for allocating socket numbers
  2546. KeepPXE        db 0            ; Should PXE be kept around?
  2547.  
  2548. ;
  2549. ; TFTP commands
  2550. ;
  2551. tftp_tail    db 'octet', 0                ; Octet mode
  2552. tsize_str    db 'tsize' ,0                ; Request size
  2553. tsize_len    equ ($-tsize_str)
  2554.         db '0', 0
  2555. blksize_str    db 'blksize', 0                ; Request large blocks
  2556. blksize_len    equ ($-blksize_str)
  2557.         asciidec TFTP_LARGEBLK
  2558.         db 0
  2559. tftp_tail_len    equ ($-tftp_tail)
  2560.  
  2561.         alignb 2, db 0
  2562. ;
  2563. ; Options negotiation parsing table (string pointer, string len, offset
  2564. ; into socket structure)
  2565. ;
  2566. tftp_opt_table:
  2567.         dw tsize_str, tsize_len, tftp_filesize
  2568.         dw blksize_str, blksize_len, tftp_blksize
  2569. tftp_opts    equ ($-tftp_opt_table)/6
  2570.  
  2571. ;
  2572. ; Error packet to return on options negotiation error
  2573. ;
  2574. tftp_opt_err    dw TFTP_ERROR                ; ERROR packet
  2575.         dw TFTP_EOPTNEG                ; ERROR 8: bad options
  2576.         db 'tsize option required', 0        ; Error message
  2577. tftp_opt_err_len equ ($-tftp_opt_err)
  2578.  
  2579.         alignb 4, db 0
  2580. ack_packet_buf:    dw TFTP_ACK, 0                ; TFTP ACK packet
  2581.  
  2582. ;
  2583. ; IP information (initialized to "unknown" values)
  2584. MyIP        dd 0            ; My IP address
  2585. ServerIP    dd 0            ; IP address of boot server
  2586. Netmask        dd 0            ; Netmask of this subnet
  2587. Gateway        dd 0            ; Default router
  2588. ServerPort    dw TFTP_PORT        ; TFTP server port
  2589.  
  2590. ;
  2591. ; Variables that are uninitialized in SYSLINUX but initialized here
  2592. ;
  2593.         alignb 4, db 0
  2594. BufSafe        dw trackbufsize/TFTP_BLOCKSIZE    ; Clusters we can load into trackbuf
  2595. BufSafeSec    dw trackbufsize/512    ; = how many sectors?
  2596. BufSafeBytes    dw trackbufsize        ; = how many bytes?
  2597. EndOfGetCBuf    dw getcbuf+trackbufsize    ; = getcbuf+BufSafeBytes
  2598. %ifndef DEPEND
  2599. %if ( trackbufsize % TFTP_BLOCKSIZE ) != 0
  2600. %error trackbufsize must be a multiple of TFTP_BLOCKSIZE
  2601. %endif
  2602. %endif
  2603. IPAppend    db 0            ; Default IPAPPEND option
  2604. DHCPMagic    db 0            ; DHCP site-specific option info
  2605.